#ifndef TEMPLATE_H
#define TEMPLATE_H
#include "doctypes.h"
#include "baskets.h"

#include <list>

namespace doc
{

	class Structure;
	class DocNode;
	typedef  design::pointer_list<Structure> StructurePtrList;
	typedef  design::aggregate_ptr_list<Structure> StructureAggList;

	class Property
	{
	public:
		enum eType {pt_name,pt_text, pt_boolean, 
			pt_list, pt_stringlist, pt_nodelist};
		static eType String2Type(const string& str);
	public:
		// construct properties using factory methods
		static Property * Create(const string& name, 
			const string& hint,
			eType type);
		static Property * Create(sixml::Node &n);
		const string& Name() const
		{ return _name; }
		const string& Hint() const
		{ return _hint; }
		const string& Default() const
		{ return _default; }
		eType Type() const
		{ return _type; }
		// AcceptValue checks wether the value is ok,
		// and may tranform it to something acceptable before
		// returning    
		virtual bool AcceptValue(string &s) const
		{ return true; }
		virtual bool IsReadOnly() const
		{ return false; }
	protected:
		virtual void Load(sixml::Node &n);
		void SetDefault(const string& value)
		{ _default = value; }
		Property(const string& name,
			const string& hint,
			eType type);

	protected:
		string _default;

	private:
		string _name;
		string _hint;
		eType _type;
	};


	// this property can take any text value
	class TextProperty : public Property
	{
	public:
		TextProperty(const string& name, const string& hint);
		virtual bool AcceptValue(string &s) const;
	};

	// this property holds valid names
	class NameProperty : public Property
	{
	public:
		NameProperty(const string& name, const string &hint);
		virtual bool AcceptValue(string &s) const;
	};

	// this property holds 'yes' and 'no'
	class BooleanProperty : public Property
	{
	public:
		BooleanProperty(const string& name, const string &hint);
		virtual bool AcceptValue(string &s) const;
	};

	// Base class for lists
	class ListProperty : public Property
	{
	public:
		ListProperty(const string& name, const string &hint);
	public:
		virtual const list<string> & Items(DocNode & n) const = 0;
		virtual bool IsStrict() const = 0;
		virtual bool IsReadOnly() const;
		virtual bool AcceptValue(string &s) const;
	protected:
		virtual void Load(sixml::Node &n) = 0;
	};

	// List of string values
	class StringListProperty : public ListProperty
	{
	public:
		StringListProperty(const string& name, const string &hint);
	public:
		virtual bool IsStrict() const
		{ return _isStrict; }
		virtual const list<string> & Items(DocNode &) const
		{ return _items; }
	protected:
		virtual void Load(sixml::Node &n);
	private:
		list<string> _items;
		bool _isStrict;
	};

	// List of string values
	class NodeListProperty : public ListProperty
	{
	public:
		NodeListProperty(const string& name, const string &hint);
	public:
		virtual bool IsStrict() const { return true; }
		virtual const list<string> & Items(DocNode &current) const;
		virtual bool IsReadOnly() const;
	protected:
		virtual void Load(sixml::Node &n);
	private:
		mutable list<string> _items;
		string _path;        	
	};

	class Structure
	{
	public:
		enum eMultiplicity {mp_01, mp_11, mp_0m, mp_1m};
		static eMultiplicity String2Multiplicity(const string& str);
	public:
		virtual eMultiplicity GetMultiplicity() const = 0;
		bool IsMandatory() const;
		bool IsOptional() const	{ return !IsMandatory(); }
		bool IsMany() const;
		bool IsOne() const { return !IsMany(); }

		virtual const StructurePtrList& Childs() const = 0;
		void GetMandatoryChilds(StructurePtrList &result) const;
		const Structure & ChildByName(const string& name) const;

		virtual bool IsNamed() const = 0;
		virtual bool HasText() const = 0;
		virtual const string& Name() const = 0;
		virtual const string& Hint() const = 0;

		virtual const pointer_list<Property> & Properties() const = 0;
		virtual Property * NameProperty() const = 0;
		Property * SecondNameProperty() const;
		virtual const string& SecondName() const = 0;
	protected:
		Property * PropertyByName(const string& propName) const;

	};

	class SimpleStructure : public Structure
	{
	public:
		SimpleStructure(const sixml::Node &, const StructurePtrList & linkables, const bool isLinkable);
		virtual const StructurePtrList& Childs() const	{ return _childs; }
		virtual const string& Name() const 	{ return _name; }
		virtual const string& Hint() const	{ return _hint; }
		virtual bool IsNamed() const { return _isNamed; }
		virtual bool HasText() const { return _hasText; }
		virtual eMultiplicity GetMultiplicity() const;		
		virtual const pointer_list<Property> & Properties() const
		{ return _properties; }
		virtual Property * NameProperty() const
		{ return _pNameProperty; }
	protected:
		virtual const string& SecondName() const
		{ return _secondNameProperty; }

	private:
		void Destroy();
		string _name;
		string _secondNameProperty;	
		string _hint;
		bool _isNamed;
		bool _hasText;
		eMultiplicity _multiplicity;
		StructureAggList _childs;
		aggregate_ptr_list<Property> _properties; // all defined properties
		Property * _pNameProperty;   // the name property (optional)
	};

	class LinkedStructure : public Structure
	{
	public:
		LinkedStructure(const Node &node,Structure &);
		virtual const StructurePtrList& Childs() const	{ return _linkTarget.Childs(); }
		virtual const string& Name() const 	{ return _linkTarget.Name(); }
		virtual const string& Hint() const	{ return _linkTarget.Hint(); }
		virtual bool IsNamed() const { return _linkTarget.IsNamed(); }
		virtual bool HasText() const { return _linkTarget.HasText(); }
		virtual eMultiplicity GetMultiplicity() const
		{ return _linkTarget.GetMultiplicity(); }
		virtual const pointer_list<Property> & Properties() const
		{ return _linkTarget.Properties(); }
		virtual Property * NameProperty() const
		{ return _linkTarget.NameProperty(); }
	protected:
		virtual const string& SecondName() const
		{ return _linkTarget.SecondName(); }

	private:
		Structure& _linkTarget;  
	};

	class Template
	{
	public:
		Template();
		void Load(const string& fileName);
		~Template();
		const Structure & structure() const;
		const string& name() const
		{ return _name; }
		// returns true if template is the base template
		bool IsTemplate() const;
	private:
		Structure * _pStructure;
		StructureAggList _linkables;
		string _name;
	};
}
bool operator == (const doc::Structure &lhs, const doc::Structure &rhs);
#endif 

